package sbc.group1.lab1.gui;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;

import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.ListModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerNumberModel;
import javax.swing.JSpinner.DefaultEditor;

import net.miginfocom.swing.MigLayout;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sbc.group1.lab1.common.Car;
import sbc.group1.lab1.common.CarBody;
import sbc.group1.lab1.common.Engine;
import sbc.group1.lab1.common.Part;
import sbc.group1.lab1.common.Wheel;
import sbc.group1.lab1.common.Engine.Type;

public abstract class AbstractFactoryPanel extends JPanel {
    private static final Logger log = LoggerFactory.getLogger(AbstractFactoryPanel.class);

    private JList listFinishedCars;
    private DefaultListModel listModelFinishedCars;

    private JSpinner spinnerEngines;

    private JSpinner spinnerWheels;

    private JSpinner spinnerCarBodies;

    private JList listCarBodiesBlank;

    private JList listCarBodiesCoated;

    private JList listEngines;

    private DefaultListModel listModelCarBodiesBlank;

    private DefaultListModel listModelCarBodiesCoated;

    private DefaultListModel listModelEngines;

    private JList listWheels;

    private DefaultListModel listModelWheels;

    private JSlider sliderWheels;

    private JSlider sliderCarBodies;

    private JSlider sliderEngines;

    private final JButton btnChooseColor;

    private Color carColor = new Color(0);

    private final JComboBox comboEngine;

    private final JSpinner spinnerCars;

    private JList listAssembled;

    private DefaultListModel listModelAssembled;

    private JList listCompleted;

    private ListModel listModelCompleted;
    private JComboBox comboEngineProduction;


    /**
     * Create the panel.
     */
    public AbstractFactoryPanel() {
        setLayout(new MigLayout("", "[54.00][150:153.00,grow][grow][194.00,grow]", "[][][][][][][][15.00][grow][grow]"));
        
        JLabel lblCount = new JLabel("Count");
        add(lblCount, "cell 0 0");
        
        JLabel lblNewLabel = new JLabel("Defect %");
        add(lblNewLabel, "cell 1 0");
        
        spinnerEngines = new JSpinner();
        spinnerEngines.setModel(new SpinnerNumberModel(new Integer(1), new Integer(1), null, new Integer(1)));
        add(spinnerEngines, "cell 0 2,growx");
        
        JButton btnProduceEngine = new JButton("Produce engines");
        btnProduceEngine.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    spinnerEngines.commitEdit();
                }
                catch (ParseException pe) {
                    JComponent editor = spinnerEngines.getEditor();
                    if (editor instanceof DefaultEditor) {
                        ((DefaultEditor)editor).getTextField().setValue(spinnerEngines.getValue());
                    }
                }
                AbstractFactoryPanel.this.asyncProduceEngines(
                        (Integer)spinnerEngines.getValue(), 
                        sliderEngines.getValue()/100.0, 
                        (Type) comboEngineProduction.getModel().getSelectedItem());
            }
        });
        
        sliderEngines = new JSlider();
        sliderEngines.setMinorTickSpacing(10);
        sliderEngines.setPaintTicks(true);
        sliderEngines.setPaintLabels(true);
        sliderEngines.setMajorTickSpacing(50);
        add(sliderEngines, "cell 1 2");
        
        comboEngineProduction = new JComboBox();
        comboEngineProduction.setModel(new DefaultComboBoxModel(Type.values()));
        add(comboEngineProduction, "cell 2 2,growx");
        add(btnProduceEngine, "cell 3 2");
        
        spinnerCarBodies = new JSpinner();
        spinnerCarBodies.setModel(new SpinnerNumberModel(new Integer(1), new Integer(1), null, new Integer(1)));
        add(spinnerCarBodies, "cell 0 3,growx");
        
        JButton btnProduceCarBodies = new JButton("Produce car bodies");
        btnProduceCarBodies.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    spinnerCarBodies.commitEdit();
                }
                catch (ParseException pe) {
                    JComponent editor = spinnerCarBodies.getEditor();
                    if (editor instanceof DefaultEditor) {
                        ((DefaultEditor)editor).getTextField().setValue(spinnerCarBodies.getValue());
                    }
                }
                AbstractFactoryPanel.this.asyncProduceCarBodies((Integer)spinnerCarBodies.getValue(), sliderCarBodies.getValue()/100.0);
            }
        });
        
        sliderCarBodies = new JSlider();
        sliderCarBodies.setPaintTicks(true);
        sliderCarBodies.setMinorTickSpacing(10);
        sliderCarBodies.setPaintLabels(true);
        sliderCarBodies.setMajorTickSpacing(50);
        add(sliderCarBodies, "cell 1 3");
        add(btnProduceCarBodies, "cell 3 3");
        
        spinnerWheels = new JSpinner();
        spinnerWheels.setModel(new SpinnerNumberModel(new Integer(4), new Integer(1), null, new Integer(4)));
        add(spinnerWheels, "cell 0 4,growx");
        
        JButton btnProduceWheels = new JButton("Produce wheels");
        btnProduceWheels.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    spinnerWheels.commitEdit();
                }
                catch (ParseException pe) {
                    JComponent editor = spinnerWheels.getEditor();
                    if (editor instanceof DefaultEditor) {
                        ((DefaultEditor)editor).getTextField().setValue(spinnerWheels.getValue());
                    }
                }
                AbstractFactoryPanel.this.asyncProduceWheels((Integer)spinnerWheels.getValue(), sliderWheels.getValue()/100.0);
            }
        });
        
        sliderWheels = new JSlider();
        sliderWheels.setPaintTicks(true);
        sliderWheels.setMinorTickSpacing(10);
        sliderWheels.setPaintLabels(true);
        sliderWheels.setMajorTickSpacing(50);
        add(sliderWheels, "cell 1 4");
        add(btnProduceWheels, "cell 3 4");
        
        spinnerCars = new JSpinner();
        spinnerCars.setModel(new SpinnerNumberModel(new Integer(1), new Integer(1), null, new Integer(1)));
        add(spinnerCars, "cell 0 5,growx");
        
        comboEngine = new JComboBox();
        comboEngine.setModel(new DefaultComboBoxModel(Type.values()));
        add(comboEngine, "cell 1 5,growx");
        
        btnChooseColor = new JButton("Choose Color");
        btnChooseColor.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                carColor = JColorChooser.showDialog(
                        AbstractFactoryPanel.this,
                        "Choose Background Color",
                        btnChooseColor.getBackground());
                btnChooseColor.setBackground(carColor);
            }
        });
        add(btnChooseColor, "cell 2 5");
        
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        add(scrollPane, "cell 0 8 4 1,grow");
        
        listFinishedCars = new JList();
        scrollPane.setViewportView(listFinishedCars);
        listModelFinishedCars = new DefaultListModel();
        listFinishedCars.setModel(listModelFinishedCars);
        
        JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
        add(tabbedPane, "cell 0 9 4 1,grow");
        
        JScrollPane scrollPane_1 = new JScrollPane();
        tabbedPane.addTab("Engines", null, scrollPane_1, null);
        
        JScrollPane scrollPane_2 = new JScrollPane();
        tabbedPane.addTab("Wheels", null, scrollPane_2, null);
        
        
        
        JScrollPane scrollPane_3 = new JScrollPane();
        tabbedPane.addTab("Car Bodies coated", null, scrollPane_3, null);
        
        JScrollPane scrollPane_4 = new JScrollPane();
        tabbedPane.addTab("Car Bodies blank", null, scrollPane_4, null);
        
        JScrollPane scrollPane_5 = new JScrollPane();
        tabbedPane.addTab("Assembled Cars", null, scrollPane_5, null);
        
        listAssembled = new JList();
        scrollPane_5.setViewportView(listAssembled);
        listModelAssembled = new DefaultListModel();
        listAssembled.setModel(listModelAssembled);
        
        JScrollPane scrollPane_6 = new JScrollPane();
        tabbedPane.addTab("Completed Cars", null, scrollPane_6, null);
        
        listCompleted = new JList();
        scrollPane_6.setViewportView(listCompleted);
        listModelCompleted = new DefaultListModel();
        listCompleted.setModel(listModelCompleted);
        
        listWheels = new JList();
        scrollPane_2.setViewportView(listWheels);
        listModelWheels = new DefaultListModel();
        listWheels.setModel(listModelWheels);
        
        listCarBodiesBlank = new JList();
        scrollPane_4.setViewportView(listCarBodiesBlank);
        listModelCarBodiesBlank = new DefaultListModel();
        listCarBodiesBlank.setModel(listModelCarBodiesBlank);
        
        listCarBodiesCoated = new JList();
        scrollPane_3.setViewportView(listCarBodiesCoated);
        listModelCarBodiesCoated = new DefaultListModel();
        listCarBodiesCoated.setModel(listModelCarBodiesCoated);
        
        listEngines = new JList();
        scrollPane_1.setViewportView(listEngines);
        listModelEngines = new DefaultListModel();
        listEngines.setModel(listModelEngines);
        


        
        JButton btnOrderCars = new JButton("Order Cars");
        btnOrderCars.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    spinnerCars.commitEdit();
                }
                catch (ParseException pe) {
                    JComponent editor = spinnerWheels.getEditor();
                    if (editor instanceof DefaultEditor) {
                        ((DefaultEditor)editor).getTextField().setValue(spinnerWheels.getValue());
                    }
                }
                onNewOrder((Integer)(spinnerCars.getValue()),(Type)comboEngine.getSelectedItem(),carColor);
            }
        });
        add(btnOrderCars, "cell 3 5");
    }
    protected abstract void onNewOrder(int numCars, Type engine, Color coatingColor);
    protected abstract void asyncProduceEngines(int quantity, double relativeDefects, Engine.Type type);
    
    protected abstract void asyncProduceWheels(int quantity, double relativeDefects);
    
    protected abstract void asyncProduceCarBodies(int quantity, double relativeDefects);

    private void addElementTo(final JList list, final Object element) {
        try {
            EventQueue.invokeAndWait(new Runnable() {
                @Override
                public void run() {
                    DefaultListModel model = (DefaultListModel) list.getModel();
                    model.addElement(element);
                }
            });
        } catch (InterruptedException e) {
            return;
        } catch (InvocationTargetException e) {
            log.error("",e);
        }
    }
    
    private void removeElementFrom(final JList list, final Object element) {
        try {
            EventQueue.invokeAndWait(new Runnable() {
                @Override
                public void run() {
                    DefaultListModel model = (DefaultListModel) list.getModel();
                    while (model.removeElement(element));
                }
            });
        } catch (InterruptedException e) {
            return;
        } catch (InvocationTargetException e) {
            log.error("",e);
        }
    }
    
    public void addPart(CarBody body) {
        if (body.isCoated()) {
            addElementTo(listCarBodiesCoated, body);
        }else {
            addElementTo(listCarBodiesBlank, body);
        }
    }
    
    public void addPart(Wheel wheel) {
        addElementTo(listWheels, wheel);
    }
    
    public void addPart(Engine engine) {
        addElementTo(listEngines, engine);
    }
    
    public void addPart(Part part) {
        if (part instanceof Wheel) {
            addPart((Wheel)part);
        }else if (part instanceof Engine) {
            addPart((Engine)part);
        }else if (part instanceof CarBody) {
            addPart((CarBody)part);
        }
    }
    public synchronized void removePart(CarBody body) {
        removeElementFrom(listCarBodiesBlank, body);
        removeElementFrom(listCarBodiesCoated, body);
    }
    
    public void removePart(Wheel wheel) {
        removeElementFrom(listWheels, wheel);
    }
    
    public void removePart(Engine engine) {
        removeElementFrom(listEngines, engine);
    }
    
    public void removePart(Part part) {
        if (part instanceof Wheel) {
            removePart((Wheel)part);
        }else if (part instanceof Engine) {
            removePart((Engine)part);
        }else if (part instanceof CarBody) {
            removePart((CarBody)part);
        }
    }
    
    public void addFinishedCar(Car car) {
        if (car.isMarkedAsFinished()) {
            addElementTo(listFinishedCars, car);
        }else {
            log.warn("Attempt to add car not marked as finished.");
        }
    }
    
    public void addAssembledCar(Car car) {
         addElementTo(listAssembled, car);
    }
    
    public void removeAssembled(Car car) {
        removeElementFrom(listAssembled, car);
    }
    
    public void addCompletedCar(Car car) {
        addElementTo(listCompleted, car);
   }
   
   public void removeCompleted(Car car) {
       removeElementFrom(listCompleted, car);
   }
}
